﻿using adb;
using Masuit.Tools;
using Masuit.Tools.Security;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MQTTnet;
using MQTTnet.Protocol;
using MQTTnet.Server;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using utils;
using Z.EntityFramework.Plus;

namespace SensorCollection
{
    public class MqttDealwith
    {

        protected adb.dbBase db = new dbBase();
        protected MqttServer mqttServer;
        protected MqttFactory mqttFactory = new MqttFactory();
        protected IConfiguration configuration;
        protected MqttServerOptions mqttOption;
        protected Dictionary<long, IDataDealwith> dealwith = new Dictionary<long, IDataDealwith>();
        protected Dictionary<string, long> jsonConvertType = new Dictionary<string, long>();
        protected Dictionary<string, MethodInfo> dealwithFun = new Dictionary<string, MethodInfo>();

        protected Dictionary<string, DateTime> lastSaveTime = new Dictionary<string, DateTime>();
        protected int AnalogTime_interval = 0;
        protected int OtherTime_interval = 0;

        protected Dictionary<string, string> powerStationClientIdlist = new Dictionary<string, string>();

        private readonly RedisUtil _redisUtil;
        public MqttDealwith(MqttServer mqttServer, IConfiguration configuration, RedisUtil redisUtil)
        {
            this.mqttServer = mqttServer;
            this.configuration = configuration;
            this._redisUtil = redisUtil;
            this.AnalogTime_interval = configuration["AnalogTime_interval"].ConvertTo<int>();
            this.OtherTime_interval = configuration["OtherTime_interval"].ConvertTo<int>();
            this.ConfigType();
            this.Init();

            //redis过期回调
            ISubscriber subscriber = redisUtil.getSubscriber();
            subscriber.Subscribe("__keyevent@0__:expired", (channel, notificationType) =>
            {
                MqttApplicationMessage applicationMessage = new MqttApplicationMessage();
                string topic = "/" + notificationType.ToString().Split(':')[1] + "/clientDisconnected";
                applicationMessage.Topic = topic;
                applicationMessage.Payload = Encoding.UTF8.GetBytes("{\"alarm\":1}");
                applicationMessage.QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
                applicationMessage.Retain = false;
                var msg = new InjectedMqttApplicationMessage(applicationMessage);
                msg.SenderClientId = "system_online";
                mqttServer.InjectApplicationMessage(msg);
            });

        }
        /// <summary>
        /// 配置获取的类型
        /// </summary>
        private void ConfigType()
        {

            using (var conn = db.CreateMyDb())
            {

                var list = conn.tPowerStationDevice.Where(x => x.dataState == 0).ToList();
                foreach (var item in list)
                {
                    jsonConvertType.Add(item.deviceTopic, item.deviceType_keyId_fk);

                    //主题上一次存库 初值
                    if (item.deviceType_keyId_fk == 820097 || item.deviceType_keyId_fk == 820094)
                    {
                        lastSaveTime.Add(item.deviceTopic, DateTime.Now.AddSeconds(-AnalogTime_interval));
                    }
                    else
                    {
                        lastSaveTime.Add(item.deviceTopic, DateTime.Now.AddMinutes(-OtherTime_interval));
                    }
                }
            }

            Assembly ab = Assembly.GetAssembly(typeof(MqttDealwith));
            Type[] types = ab.GetTypes();
            foreach (Type item in types)
            {
                if (item.GetInterface("IDataDealwith") != null && !item.IsAbstract)
                {
                    IDataDealwith dealwithBase = (IDataDealwith)item.GetConstructor(new[] { typeof(MqttServer), typeof(DbContextOptionsBuilder<DeviceDbContext>), typeof(DbContextOptionsBuilder<MyDbContext>) }).Invoke(new object[] { mqttServer, db.deviceoOptionsBuilder, db.myOptionsBuilder });
                    if (dealwithBase != null)
                    {
                        dealwith[dealwithBase.keyId] = dealwithBase;
                    }
                }
            }
        }
        private void Init()
        {
            this.mqttServer.StartedAsync += MqttServer_StartedAsync;
            this.mqttServer.ClientConnectedAsync += MqttServer_ClientConnectedAsync;
            this.mqttServer.ClientDisconnectedAsync += MqttServer_ClientDisconnectedAsync;
            this.mqttServer.ClientSubscribedTopicAsync += MqttServer_ClientSubscribedTopicAsync;
            this.mqttServer.ApplicationMessageNotConsumedAsync += MqttServer_ApplicationMessageNotConsumedAsync;
            this.mqttServer.ValidatingConnectionAsync += MqttServer_ValidatingConnectionAsync;
            this.mqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync;
            this.mqttServer.InterceptingPublishAsync += StopDisconnectClient;
        }



        private async Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs e)
        {
            try
            {
                using (var conn = db.CreateMyDb())
                {

                    if (e.ClientId.StartsWith("web_"))
                    {
                        var m = await conn.tUser.FirstOrDefaultAsync(x => x.account == e.Username);
                        if (m == null || m.password != e.Password.MDString())
                        {
                            e.ReasonCode = MQTTnet.Protocol.MqttConnectReasonCode.BadUserNameOrPassword;
                            e.ReasonString = "设备未绑定到系统";
                            Console.WriteLine($"{e.ClientId}绑定不成功");
                        }
                    }
                    else
                    {
                        bool ok = e.Username == "admin" && e.Password == "admin";
                        if (this.configuration["SystemUser"].Contains(e.ClientId))
                        {
                            Console.WriteLine($"系统客户端({e.ClientId})接入");
                            return;
                        }
                        var deviceModel = await conn.tDevice.FirstOrDefaultAsync(x => x.dataState == 0 && x.account == e.Username && x.password == e.Password);
                        ok = ok || deviceModel != null;
                        if (ok == false)
                        {
                            e.ReasonCode = MQTTnet.Protocol.MqttConnectReasonCode.BadUserNameOrPassword;
                            e.ReasonString = "设备未绑定到系统";
                            Console.WriteLine($"{e.ClientId}绑定不成功");
                        }
                        else
                        {
                            e.SessionItems["deviceId"] = deviceModel.globalDeviceId;
                            e.SessionItems["deviceSN"] = deviceModel.deviceSN;
                            var powerStation = await conn.tPowerStation.FirstOrDefaultAsync(x => x.dataState == 0 && x.powerStationId == deviceModel.station_device_powerStationId_fk);
                            if (powerStationClientIdlist.ContainsKey(e.ClientId))
                            {
                                powerStationClientIdlist.Remove(e.ClientId);
                            }
                            powerStationClientIdlist.Add(e.ClientId, powerStation.powerStationNo);
                            _redisUtil.Remove("offline:" + powerStation.powerStationNo);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                writeExceptionLogUtil.writeLogException(ex, "/root/logs", "SensorCollection");
                Console.WriteLine($"验证用户:" + ex.Message);
                throw ex;
            }
        }


        private async Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs e)
        {
            //try
            //{
            //    Console.WriteLine($"收到SN:{e.ClientId}信息\n主题:{e.ApplicationMessage.Topic}\n内容:{Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
            //}
            //catch (Exception ex) { 

            //}

            if (jsonConvertType.ContainsKey(e.ApplicationMessage.Topic))
            {
                string jsonData = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
                if (e.SessionItems["deviceSN"] == null) return;
                string deviceSN = e.SessionItems["deviceSN"].ToString();
                //if (e.ApplicationMessage.Topic.Contains("control"))
                //{
                //    Console.WriteLine($"收到SN:{e.ClientId}信息\n主题:{e.ApplicationMessage.Topic}\n内容处理:{Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
                //}
                try
                {
                    IDataDealwith actionModel = dealwith[jsonConvertType[e.ApplicationMessage.Topic]];


                    //首先按电站分组，然后套keyid，接下去存topic和json值  



                    //if (actionModel.keyId == 820089 || actionModel.keyId == 820002 || actionModel.keyId == 820075 || actionModel.keyId == 820090 || actionModel.keyId == 820091 || actionModel.keyId == 820092 || actionModel.keyId == 820093 || actionModel.keyId == 820094 || actionModel.keyId == 820095 || actionModel.keyId == 820096 || actionModel.keyId == 820097 || actionModel.keyId == 820098)
                    //{
                    //    string SN = e.ApplicationMessage.Topic.Split("/")[1] + ":"+ actionModel.keyId + ":" + e.ApplicationMessage.Topic.Split("/")[2];
                    //    await _redisUtil.SetKeyExpire(SN, jsonData, 30);
                    //}
                    //820002  820110   820096
                    List<int> typeList = new List<int>() { 820002, 820110, 820096 };
                    if (!typeList.Contains((int)actionModel.keyId))
                    {
                        await _redisUtil.SetKeyExpire(e.ApplicationMessage.Topic, jsonData, 30);
                    }

                    if (actionModel.keyId == 820097 || actionModel.keyId == 820094)
                    {
                        if ((DateTime.Now - lastSaveTime[e.ApplicationMessage.Topic]).TotalSeconds < AnalogTime_interval)
                        {
                            return;
                        }
                    }
                    else
                    {

                        if ((DateTime.Now - lastSaveTime[e.ApplicationMessage.Topic]).TotalMinutes < OtherTime_interval)
                        {
                            return;
                        }

                    }

                    var m = actionModel.JsonToModel(jsonData, deviceSN);
                    if (m != null)
                    {
                        lastSaveTime[e.ApplicationMessage.Topic] = DateTime.Now;
                        await actionModel.ReactData(m, e.ApplicationMessage.Topic, deviceSN);
                    }

                }
                catch (Exception ex)
                {
                    writeExceptionLogUtil.writeLogException(ex, "/root/logs", "SensorCollection");
                    Console.WriteLine(ex.Message);
                    Console.WriteLine($"收到SN:{e.ClientId}信息\n主题:{e.ApplicationMessage.Topic}\n内容处理失败:{Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
                }
            }
            else
            {
                if (("/system/device/reload-device").Equals(e.ApplicationMessage.Topic))
                {
                    Console.WriteLine($"收到重载设备信息\n主题:{e.ApplicationMessage.Topic},信息:{Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
                    lastSaveTime.Clear();
                    jsonConvertType.Clear();
                    using (var conn = db.CreateMyDb())
                    {
                        var list = conn.tPowerStationDevice.Where(x => x.dataState == 0).ToList();
                        foreach (var item in list)
                        {
                            jsonConvertType.Add(item.deviceTopic, item.deviceType_keyId_fk);

                            //主题上一次存库 初值
                            if (item.deviceType_keyId_fk == 820097 || item.deviceType_keyId_fk == 820094)
                            {
                                lastSaveTime.Add(item.deviceTopic, DateTime.Now.AddSeconds(-AnalogTime_interval));
                            }
                            else
                            {
                                lastSaveTime.Add(item.deviceTopic, DateTime.Now.AddMinutes(-OtherTime_interval));
                            }
                        }
                    }
                    Console.WriteLine($"重载设备完成");
                }
            }

            //return Task.CompletedTask;
        }
        /// <summary>
        /// 把客户端跳下线
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private async Task StopDisconnectClient(InterceptingPublishEventArgs e)
        {
            var kk = 0;
            //throw new Exception("已经无法实现了,2023-02-12");
            //if (e.ApplicationMessage.Topic.StartsWith("/bz/stop/device"))
            //{
            //    string deviceSN = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
            //    using (var conn = this.db.CreateMyDb()) {
            //        await conn.tPowerStationDevice.Where(x => x.deviceSN == deviceSN).UpdateAsync(x => new tPowerStationDevice() { deviceState = 3 });
            //        await conn.SaveChangesAsync();
            //        try
            //        {
            //            await mqttServer.DisconnectClientAsync(deviceSN, MQTTnet.Protocol.MqttDisconnectReasonCode.ServerMoved);
            //        }
            //        catch (Exception ex) {
            //            Console.WriteLine($"断开用户端出错:{ex.Message}");
            //        }
            //        var msg = this.mqttFactory.CreateApplicationMessageBuilder();
            //        msg.WithTopic($"/bz/stop/device/"+deviceSN);
            //        msg.WithPayload("OK");
            //        await mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(msg.Build()));
            //    }
            //}
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private async Task MqttServer_ApplicationMessageNotConsumedAsync(ApplicationMessageNotConsumedEventArgs e)
        {
            if (e.ApplicationMessage.Topic != TopicBase.alternatorDataTopic)
            {
                //Console.WriteLine($"主题{e.ApplicationMessage.Topic},SN:{e.SenderId},信息未发送成功");
            }
            //if (e.ApplicationMessage.Topic.StartsWith("/bz/switch/down/"))
            //{
            //    using (var conn = db.CreateDeviceDb())
            //    {
            //        tSwitchDeviceData model = System.Text.Json.JsonSerializer.Deserialize<tSwitchDeviceData>(e.ApplicationMessage.Payload);
            //        if (model != null)
            //        {
            //            await conn.tSwitchDeviceData.DeleteByKeyAsync(model.switchDeviceDataId);
            //            await conn.SaveChangesAsync();
            //        }
            //    }
            //}

            //未发送过的消息
            //return Task.CompletedTask;
        }

        private async Task MqttServer_ClientSubscribedTopicAsync(ClientSubscribedTopicEventArgs e)
        {
            Console.WriteLine($"有客户{e.ClientId}订阅{e.TopicFilter.Topic}成功");
            //return Task.CompletedTask;
        }

        private async Task MqttServer_ClientDisconnectedAsync(ClientDisconnectedEventArgs e)
        {
            //using (var conn = db.CreateMyDb())
            //{
            //    await conn.tPowerStationDevice.Where(x => x.deviceSN == e.ClientId).UpdateAsync(x=>new tPowerStationDevice() { deviceState = 1});
            //    await conn.SaveChangesAsync();
            //}
            Console.WriteLine($"有客户{e.ClientId}退出");
            if (!powerStationClientIdlist.ContainsKey(e.ClientId)) return;
            double offlineInterval = configuration["offline_interval"].ConvertTo<double>();
            await _redisUtil.SetKeyExpire("offline:" + powerStationClientIdlist[e.ClientId], "", offlineInterval);
            powerStationClientIdlist.Remove(e.ClientId);
        }

        private async Task MqttServer_ClientConnectedAsync(ClientConnectedEventArgs arg)
        {
            Console.WriteLine($"有客户{arg.ClientId}连接成功,Username:{arg.UserName}");
            //return Task.CompletedTask;
        }

        private async Task MqttServer_StartedAsync(EventArgs arg)
        {
            Console.WriteLine($"服务器已经开启:{mqttOption?.DefaultEndpointOptions.BoundInterNetworkAddress}:{mqttOption?.DefaultEndpointOptions.Port}");
            //return Task.CompletedTask;
        }


    }

}
